#include <iostream>
#include <iomanip>
#include <memory>
#include <stdio.h>

#include <taglib/id3v2tag.h>
#include <taglib/mpegfile.h>
#include <taglib/flacfile.h>
#include <taglib/vorbisfile.h>
#include <taglib/id3v2frame.h>
#include <taglib/id3v2header.h>
#include <taglib/attachedpictureframe.h>

using namespace std;
using namespace TagLib;

void usage(string message) {
    cerr << "usage: id3-extract-cover MP3|FLAC|OGG" << "\n";
    cerr << message << "\n";
    exit(EXIT_FAILURE);
}

int main(int argc, char **argv) {
    if (argc < 2)
        usage("error: filename required");

    string str(argv[1]);
    /* string conversion */
    transform(str.begin(), str.end(), str.begin(),
        [](unsigned char c){ return tolower(c); });

    vector<shared_ptr<File>> files;
    shared_ptr<File> mpeg_file(new MPEG::File(argv[1]));
    shared_ptr<File> flac_file(new FLAC::File(argv[1]));
    shared_ptr<File> oggv_file(new Ogg::Vorbis::File(argv[1]));
    files.push_back(move(mpeg_file));
    files.push_back(move(flac_file));
    files.push_back(move(oggv_file));

    for (vector<shared_ptr<File>>::iterator files_it = std::begin(files); files_it != std::end(files); ++files_it) {
        /* try to cast file into mpeg */
        if (std::shared_ptr<MPEG::File> mpeg_p = std::dynamic_pointer_cast<MPEG::File>(*files_it)) {
            ID3v2::Tag *id3v2tag = (*mpeg_p).ID3v2Tag();
            if (!id3v2tag)
                usage("file does not contain an id3v2 tag");

            ID3v2::FrameList frame = id3v2tag->frameList();
            for (ID3v2::FrameList::ConstIterator it = frame.begin(); it != frame.end(); ++it) {
                /* try to cast picture into picture frame */
                if (ID3v2::AttachedPictureFrame* pic_frame = dynamic_cast<ID3v2::AttachedPictureFrame*>(*it)) {
                    auto picture_bytes = pic_frame->picture();
                    cout << picture_bytes;
                    return EXIT_SUCCESS;
                }
            }
        }

        /* try to cast file into flac */
        if (std::shared_ptr<FLAC::File> flac_p = std::dynamic_pointer_cast<FLAC::File>(*files_it)) {
            /* id3v2 route */
            if (flac_p->hasID3v2Tag()) {
                ID3v2::Tag *id3v2tag = (*flac_p).ID3v2Tag();

                ID3v2::FrameList frame = id3v2tag->frameList();
                for (ID3v2::FrameList::ConstIterator it = frame.begin(); it != frame.end(); ++it) {
                    /* try to cast picture into picture frame */
                    if (ID3v2::AttachedPictureFrame* pic_frame = dynamic_cast<ID3v2::AttachedPictureFrame*>(*it)) {
                        auto picture_bytes = pic_frame->picture();
                        cout << picture_bytes;
                        return EXIT_SUCCESS;
                    }
                }
            /* ogg::xiphcomment route */
            } else {
                if (!flac_p->tag())
                    usage("file does not contain a xiph tag");

                if (flac_p->pictureList().front()) {
                    auto picture_bytes = flac_p->pictureList().front()->data();
                    cout << picture_bytes;
                    return EXIT_SUCCESS;
                }
            }
        }

        /* try to cast file into ogg vorbis */
        if (std::shared_ptr<Ogg::Vorbis::File> oggv_p = std::dynamic_pointer_cast<Ogg::Vorbis::File>(*files_it)) {
            if (!oggv_p->tag())
                usage("file does not contain a xiph tag");

            if (oggv_p->tag()->pictureList().front()) {
                auto picture_bytes = oggv_p->tag()->pictureList().front()->data();
                cout << picture_bytes;
                return EXIT_SUCCESS;
            }
        }
    }
   
    return EXIT_SUCCESS;
}